/*
 * Copyright (c) Tony Bybell 2001-2010
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 */

#include <config.h>
#include "globals.h"
#include <stdio.h>

#include <unistd.h>
#ifndef __MINGW32__
#include <sys/mman.h>
#endif

#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "symbol.h"
#include "vcd.h"
#include "lxt.h"
#include "bsearch.h"
#include "hierpack.h"

#ifdef _WAVE_HAVE_JUDY
#include <Judy.h>
#endif

/*
 * s_selected accessors
 */
#ifdef _WAVE_HAVE_JUDY

char get_s_selected(struct symbol *s)
{
    int rc = Judy1Test(GLOBALS->s_selected, (Word_t)s, PJE0);

    return (rc);
}

char set_s_selected(struct symbol *s, char value)
{
    if (value) {
        Judy1Set((Pvoid_t)&GLOBALS->s_selected, (Word_t)s, PJE0);
    } else {
        Judy1Unset((Pvoid_t)&GLOBALS->s_selected, (Word_t)s, PJE0);
    }

    return (value);
}

void destroy_s_selected(void)
{
    Judy1FreeArray(&GLOBALS->s_selected, PJE0);

    GLOBALS->s_selected = NULL;
}

#else

char get_s_selected(struct symbol *s)
{
    return (s->s_selected);
}

char set_s_selected(struct symbol *s, char value)
{
    return ((s->s_selected = value));
}

void destroy_s_selected(void)
{
    /* nothing */
}

#endif

/*
 * hash create/destroy
 */
void sym_hash_initialize(void *g)
{
#ifdef _WAVE_HAVE_JUDY
    ((struct Global *)g)->sym_judy = NULL;
#else
    ((struct Global *)g)->sym_hash = (struct symbol **)calloc_2(SYMPRIME, sizeof(struct symbol *));
#endif
}

void sym_hash_destroy(void *g)
{
    struct Global *gg = (struct Global *)g;

#ifdef _WAVE_HAVE_JUDY

    JudySLFreeArray(&gg->sym_judy, PJE0);
    gg->sym_judy = NULL;

#else

    if (gg->sym_hash) {
        free_2(gg->sym_hash);
        gg->sym_hash = NULL;
    }

#endif
}

/*
 * Generic hash function for symbol names...
 */
int hash(char *s)
{
#ifndef _WAVE_HAVE_JUDY
    char *p;
    char ch;
    unsigned int h = 0, h2 = 0, pos = 0, g;
    for (p = s; *p; p++) {
        ch = *p;
        h2 <<= 3;
        h2 -= ((unsigned int)ch + (pos++)); /* this handles stranded vectors quite well.. */

        h = (h << 4) + ch;
        if ((g = h & 0xf0000000)) {
            h = h ^ (g >> 24);
            h = h ^ g;
        }
    }

    h ^= h2; /* combine the two hashes */
    GLOBALS->hashcache = h % SYMPRIME;
#else
    (void)s;
#endif
    return (GLOBALS->hashcache);
}

/*
 * add symbol to table.  no duplicate checking
 * is necessary as aet's are "correct."
 */
struct symbol *symadd(char *name, int hv)
{
    struct symbol *s = (struct symbol *)calloc_2(1, sizeof(struct symbol));

#ifdef _WAVE_HAVE_JUDY
    (void)hv;

    PPvoid_t PPValue = JudySLIns(&GLOBALS->sym_judy, (uint8_t *)name, PJE0);
    *((struct symbol **)PPValue) = s;

#else

    strcpy(s->name = (char *)malloc_2(strlen(name) + 1), name);
    s->sym_next = GLOBALS->sym_hash[hv];
    GLOBALS->sym_hash[hv] = s;

#endif
    return (s);
}

struct symbol *symadd_name_exists(char *name, int hv)
{
    struct symbol *s = (struct symbol *)calloc_2(1, sizeof(struct symbol));

#ifdef _WAVE_HAVE_JUDY
    (void)hv;

    PPvoid_t PPValue = JudySLIns(&GLOBALS->sym_judy, (uint8_t *)name, PJE0);
    *((struct symbol **)PPValue) = s;

    s->name = name; /* redundant for now */

#else

    s->name = name;
    s->sym_next = GLOBALS->sym_hash[hv];
    GLOBALS->sym_hash[hv] = s;

#endif

    return (s);
}

/*
 * find a slot already in the table...
 */
static struct symbol *symfind_2(char *s, unsigned int *rows_return)
{
#ifndef _WAVE_HAVE_JUDY
    int hv;
    struct symbol *temp;
#endif

    if (!GLOBALS->facs_are_sorted) {
#ifdef _WAVE_HAVE_JUDY
        PPvoid_t PPValue = JudySLGet(GLOBALS->sym_judy, (uint8_t *)s, PJE0);

        if (PPValue) {
            return (*(struct symbol **)PPValue);
        }
#else
        hv = hash(s);
        if (!(GLOBALS->sym_hash) || !(temp = GLOBALS->sym_hash[hv]))
            return (NULL); /* no hash entry, add here wanted to add */

        while (temp) {
            if (!strcmp(temp->name, s)) {
                return (temp); /* in table already */
            }
            if (!temp->sym_next)
                break;
            temp = temp->sym_next;
        }
#endif
        return (NULL); /* not found, add here if you want to add*/
    } else /* no sense hashing if the facs table is built */
    {
        struct symbol *sr;
        DEBUG(printf("BSEARCH: %s\n", s));

        sr = bsearch_facs(s, rows_return);
        if (sr) {
        } else {
            /* this is because . is > in ascii than chars like $ but . was converted to 0x1 on sort
             */
            char *s2;
            int i;
            int mat;

            if (!GLOBALS->escaped_names_found_vcd_c_1) {
                return (sr);
            }

            if (GLOBALS->facs_have_symbols_state_machine == 0) {
                if (GLOBALS->escaped_names_found_vcd_c_1) {
                    mat = 1;
                } else {
                    mat = 0;

                    for (i = 0; i < GLOBALS->numfacs; i++) {
                        int was_packed = HIER_DEPACK_STATIC;
                        char *hfacname = NULL;

                        hfacname = hier_decompress_flagged(GLOBALS->facs[i]->name, &was_packed);
                        s2 = hfacname;
                        while (*s2) {
                            if (*s2 < GLOBALS->hier_delimeter) {
                                mat = 1;
                                break;
                            }
                            s2++;
                        }

                        /* if(was_packed) { free_2(hfacname); } ...not needed with
                         * HIER_DEPACK_STATIC */
                        if (mat) {
                            break;
                        }
                    }
                }

                if (mat) {
                    GLOBALS->facs_have_symbols_state_machine = 1;
                } else {
                    GLOBALS->facs_have_symbols_state_machine = 2;
                } /* prevent code below from executing */
            }

            if (GLOBALS->facs_have_symbols_state_machine == 1) {
                mat = 0;
                for (i = 0; i < GLOBALS->numfacs; i++) {
                    int was_packed = HIER_DEPACK_STATIC;
                    char *hfacname = NULL;

                    hfacname = hier_decompress_flagged(GLOBALS->facs[i]->name, &was_packed);
                    if (!strcmp(hfacname, s)) {
                        mat = 1;
                    }

                    /* if(was_packed) { free_2(hfacname); } ...not needed with HIER_DEPACK_STATIC */
                    if (mat) {
                        sr = GLOBALS->facs[i];
                        break;
                    }
                }
            }
        }

        return (sr);
    }
}

struct symbol *symfind(char *s, unsigned int *rows_return)
{
    struct symbol *s_pnt = symfind_2(s, rows_return);

    if (!s_pnt) {
        int len = strlen(s);
        if (len) {
            char ch = s[len - 1];
            if ((ch != ']') && (ch != '}')) {
                char *s2 = g_alloca(len + 4);
                memcpy(s2, s, len);
                strcpy(s2 + len, "[0]"); /* bluespec vs modelsim */

                s_pnt = symfind_2(s2, rows_return);
            }
        }
    }

    return (s_pnt);
}
